package org.ithang.tools.dao;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.ithang.tools.dao.meta.Column;
import org.ithang.tools.dao.meta.ID;
import org.ithang.tools.dao.meta.Ignore;
import org.ithang.tools.dao.meta.Keyword;
import org.ithang.tools.dao.meta.Table;
import org.ithang.tools.model.Page;
import org.ithang.tools.model.TypeException;
import org.ithang.tools.util.StrUtils;
import org.springframework.jdbc.core.RowMapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ModelDao<T> extends BaseDao {

	private Class<T> cls;
	private RowMapper<T> rowMapper;
	
	@SuppressWarnings("unchecked")
	public ModelDao() {
		if(this.getClass().getGenericSuperclass() instanceof ParameterizedType) {
			ParameterizedType type=(ParameterizedType)this.getClass().getGenericSuperclass();
			cls=(Class<T>)type.getActualTypeArguments()[0];
			rowMapper=new RowMapper<T>() {
				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					try {
						T obj=cls.getDeclaredConstructor().newInstance();
						Field[] fields=cls.getDeclaredFields();
						for(Field field:fields) {
							if(!field.isAnnotationPresent(Ignore.class)) {
								field.setAccessible(true);
								field.set(obj, rs.getObject(StrUtils.addUnderline(field.getName())));
							}
						}
						return obj;
					}catch(Exception e) {
						e.printStackTrace();
					}
					
					return null;
				}
				
			};
		}else {
			throw new TypeException("没有设置有效的泛型类型!");
		}
	}
	
	
	public T get(Object id){
		StringBuilder sber=new StringBuilder("select * from ");
		sber.append(getTableName()).append(" where ").append(getPrimaryColumnName()).append("=?");
		return getsBean(sber.toString(), getMapper(), id);
	}
	
	
	public Page<T> query(Page<T> page){
		StringBuilder sber=new StringBuilder("select * from ");
		StringBuilder counter=new StringBuilder("select count(0) from ");
		//获取表名称 start
		String tableName=cls.getSimpleName();
		if(StrUtils.isNotBlank(tableName)) {//默认:使用小写驼峰式  
			tableName=StrUtils.addUnderline(tableName);
		}
		
		if(cls.isAnnotationPresent(Table.class)) {
			String tempName=cls.getAnnotation(Table.class).value();
			if(!StrUtils.isNotBlank(tempName)) {
				log.error("Class:{}上的注解@Table未设置表名",cls.getSimpleName());
			}else {
				tableName=tempName;
			}
		}
		counter.append(tableName);
		int current=page.getPage()<=0?0:(page.getPage()-1)*page.getSize();
		sber.append(tableName);
		if(null!=page.getKeyword()&&page.getKeyword().length>0) {
			String where=where(page.getKeyword());
			if(StrUtils.isNotBlank(where)) {
				sber.append(where);
				counter.append(where);
			}
		}
		
		if(null!=page.getOrderBy()&&0!=page.getOrderBy().trim().length()) {
			sber.append(" order by ").append(page.getOrderBy());
			sber.append(" ").append(page.isAsc()?"asc":"desc");
		}
		sber.append(" limit ").append(current).append(",").append(page.getSize());
		List<T> data=listsBean(sber.toString(),rowMapper);
		page.setData(data);
		long total=getsLong(counter.toString());
		page.setTotal(total);
		return page;
	}
	
	public Page<T> query(int pageNum,int pageSize,Object... keyword){
		Page<T> page=new Page<>();
		page.setSize(pageSize);
		page.setPage(pageNum);
		page.setKeyword(keyword);
		return query(page);
	}
	
	public String where(Object... keywords) {
		if(null==keywords||0==keywords.length) {
			return "";
		}
		StringBuilder where=new StringBuilder(" where 1=1 ");
		Field[] fields=cls.getDeclaredFields();
		int index=0;
		for(Field field:fields) {
			if(field.isAnnotationPresent(Keyword.class)) {
				if(null!=keywords[index]) {
					where.append(" and ");
					where.append(getColumnName(field));
					Keyword kwd=field.getDeclaredAnnotation(Keyword.class);
					if(kwd.like()) {
						where.append(" like '%").append(keywords[index]).append("%'");
					}else {
						if(field.getType() == String.class) {
							where.append(" = '").append(keywords[index]).append("'");
						}else {
							where.append(" = ").append(keywords[index]);
						}
					}
				}
				index++;
			}
		}
		return where.toString();
	}
	
	
	/**
	 * 快速新增方法
	 * @param beanMap
	 * @param beanCls
	 * @return
	 */
	public int insert(Map<String,Object> beanMap,Class<T> beanCls) {
		StringBuilder sber=new StringBuilder("insert into ");
		
		sber.append(getTableName()).append("(");
		
		Field[] fields=beanCls.getDeclaredFields();
		for(Field field:fields) {
			if(beanMap.containsKey(field.getName())) {
				sber.append(StrUtils.addUnderline(field.getName())).append(",");
			}
		}
		sber=sber.delete(sber.length()-1, sber.length());
		sber.append(")values(");
		for(Field field:fields) {
			if(beanMap.containsKey(field.getName())) {
			    sber.append(":").append(field.getName()).append(",");
			}
		}
		sber=sber.delete(sber.length()-1, sber.length());
		sber.append(")");
	    return updateSQL(sber.toString(), beanMap);
	}
	
	/**
	 * 快速新增方法
	 * @param bean
	 * @return
	 */
	public int insert(T bean) {
		StringBuilder sber=new StringBuilder("insert into ");
		sber.append(getTableName()).append("(");

		boolean first=true;
		Field[] fields=bean.getClass().getDeclaredFields();
		for(Field field:fields) {
			if(field.isAnnotationPresent(ID.class)&&field.getAnnotation(ID.class).ignor()) {
				continue;
			}
			if(field.isAnnotationPresent(Ignore.class)) {
				continue;
			}
			if(!first) {
				sber.append(",");
			}
			sber.append(getColumnName(field));
			first=false;
		}
		sber.append(")values(");
		first=true;
		for(Field field:fields) {
			if(field.isAnnotationPresent(ID.class)&&field.getAnnotation(ID.class).ignor()) {
				continue;
			}
			if(field.isAnnotationPresent(Ignore.class)) {
				continue;
			}
			if(!first) {
				sber.append(",");
			}
			field.setAccessible(true);
			sber.append(":").append(field.getName());
			first=false;
		}
		sber.append(")");
	    return updateSQL(sber.toString(),StrUtils.objToMap(bean));
	}
	
	/**
	 * 快速更新方法
	 * @param beanMap
	 * @param beanCls
	 * @return
	 */
	public int update(Map<String,Object> beanMap,Class<T> beanCls) {
		StringBuilder sber=new StringBuilder("update ");
		sber.append(getTableName()).append(" set ");
		
		Field[] fields=beanCls.getDeclaredFields();		
		for(Field field:fields) {
			if(!field.isAnnotationPresent(ID.class)&&!field.isAnnotationPresent(Ignore.class)) {
				if(beanMap.containsKey(field.getName())) {
					sber.append(StrUtils.addUnderline(field.getName())).append("=:").append(field.getName()).append(",");	
				}
			}
		}
		sber=sber.delete(sber.length()-1, sber.length());
		sber.append(" where ").append(getPrimaryColumnName()).append("=:").append(getPrimaryField().getName());
		
	    return updateSQL(sber.toString(), beanMap);
	}
	
	public int update(T bean) {
		StringBuilder sber=new StringBuilder("update ");
		sber.append(getTableName()).append(" set ");
		
		Field[] fields=bean.getClass().getDeclaredFields();
		boolean first=true;
		for(Field field:fields) {
			field.setAccessible(true);
			if(!field.isAnnotationPresent(ID.class)&&!field.isAnnotationPresent(Ignore.class)) {
				if(!first) {
					sber.append(",");
				}
				sber.append(getColumnName(field)).append("=:").append(field.getName());
				first=false;
			}
		}
		sber.append(" where ").append(getPrimaryColumnName()).append("=:").append(null==getPrimaryField()?"id":getPrimaryField().getName());
		
	    return updateSQL(sber.toString(),StrUtils.objToMap(bean));
	
	}
	
	public int delete(Object id) {
		StringBuilder sber=new StringBuilder("delete from ");
		sber.append(getTableName()).append(" where ").append(getPrimaryColumnName()).append("=?");
		return updatesSQL(sber.toString(), id);
	}
	
	public int disable(Object id) {
		StringBuilder sber=new StringBuilder("update ");
		sber.append(getTableName()).append(" set deleted=1 where ").append(getPrimaryColumnName()).append("=?");
		return updatesSQL(sber.toString(), id);
	}
	
	
	
	///===========================private method===============================
	
	protected RowMapper<T> getMapper() {
		return this.rowMapper;
	}
	
	protected Class<T> getCls(){
		return this.cls;
	}
	
	protected String getTableName() {
		String tableName=getCls().getSimpleName();
		if(StrUtils.isNotBlank(tableName)) {//默认:使用小写驼峰式  
			tableName=StrUtils.addUnderline(tableName);
		}
		
		if(getCls().isAnnotationPresent(Table.class)) {
			String tempName=getCls().getAnnotation(Table.class).value();
			if(!StrUtils.isNotBlank(tempName)) {
				log.error("Class:{}上的注解@Table未设置表名",getCls().getSimpleName());
			}else {
				tableName=tempName;
			}
		}
		log.info("{}'s TableName is {}",getCls().getSimpleName(),tableName);
		return tableName;
	}
	
	protected String getPrimaryColumnName() {
		String idName="id";
		Field[] fields=getCls().getDeclaredFields();
		for(Field field:fields) {
			if(field.isAnnotationPresent(ID.class)) {
				String tempName=field.getAnnotation(ID.class).value();
				if(!StrUtils.isNotBlank(tempName)) {//如果没有值，则转字段名为列名
					idName=StrUtils.addUnderline(field.getName());
				}else {
					idName=tempName;
				}
			}
		}
		return idName;
	}
	
	protected Field getPrimaryField() {
		Field idField=null;
		Field[] fields=getCls().getDeclaredFields();
		for(Field field:fields) {
			if(field.isAnnotationPresent(ID.class)) {
				idField=field;
			}
		}
		return idField;
	}
	
	protected String getColumnName(Field field) {
		if(field.isAnnotationPresent(Column.class)) {
			return field.getAnnotation(Column.class).value();
		}else {
			return StrUtils.addUnderline(field.getName());
		}
	}
	
	
}
